package apes.lib; import java.util.HashSet; import java.util.Set; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; import javax.sound.sampled.FloatControl; import javax.sound.sampled.SourceDataLine; import apes.models.InternalFormat; import apes.models.Player; /** * Wrapper for all players. So instead of calling play on a Player, play in this * class is called. It also handles the volume. * * @author Johan Andersson (johandy@student.chalmers.se) */ public class PlayerHandler { /** * Current volume. */ private int volume; /** * Minimum value for the player. */ public static final int MIN_VALUE = 0; /** * Maximum value for the player. */ public static final int MAX_VALUE = 100; /** * Represents a special kind of data line whose audio data can be loaded prior * to playback. */ private SourceDataLine line; /** * Keeps track over floating-point values. */ private FloatControl gainControl; /** * All players that currently exists. */ private Set<Player> players; /** * The player that is playing. */ private Player currentPlayer; /** * Temporary internal format. See usage in code. */ private InternalFormat internalFormat; /** * An instance of this class. */ private static PlayerHandler instance = null; /** * Creates a new <code>PlayerHandler</code>. */ private PlayerHandler() { players = new HashSet<Player>(); } /** * Does some initialization such as fetching the line and getting volume * control. NOTE: {@link PlayerHandler#setInternalFormat setInternalFormat} * must be called before this. */ private void init() { try { AudioFormat format = new AudioFormat(internalFormat.getSampleRate(), internalFormat.bitsPerSample, internalFormat.getNumChannels(), true, false); DataLine.Info info = new DataLine.Info(SourceDataLine.class, format); if(line != null) { line.close(); } line = (SourceDataLine)AudioSystem.getLine(info); line.open(format); line.start(); // Get volume control. gainControl = (FloatControl)line.getControl(FloatControl.Type.MASTER_GAIN); } catch(Exception e) { e.printStackTrace(); } } /** * Plays the current player. */ public void play() { if(currentPlayer != null) { currentPlayer.play(); } } /** * Pauses the current player. */ public void pause() { if(currentPlayer != null) { currentPlayer.pause(); } } /** * Stops the current player. */ public void stop() { if(currentPlayer != null) { currentPlayer.stop(); } } /** * Go forward. */ public void forward() { if(currentPlayer != null) { currentPlayer.forward(); } } /** * Go backward. */ public void backward() { if(currentPlayer != null) { currentPlayer.backward(); } } /** * Get the current volume. * * @return the volume. */ public int getVolume() { return volume; } /** * Set the volume. * * @param volume The new volume. */ public void setVolume(int volume) { if(volume >= MIN_VALUE && volume <= MAX_VALUE) { this.volume = volume; float value = (float)(Math.log(this.volume / 100.0) / Math.log(10.0) * 20.0); if(gainControl != null) { gainControl.setValue(value); } } } /** * Return the internal format connected to the current player. * * @return The internal format. */ public InternalFormat getInternalFormat() { if(currentPlayer != null) { return currentPlayer.getInternalFormat(); } return null; } /** * Sets a new internal format. If a player exists with that internalFormat, it * is used. Otherwise a new Player is created. * * @param internalFormat an <code>InternalFormat</code> value * @return The player that is associated with <code>internalFormat</code>. */ public Player setInternalFormat(InternalFormat internalFormat) { this.internalFormat = internalFormat; // Pause if there's a player. if(currentPlayer != null) { currentPlayer.pause(); } // Get or create a new player. Player player = getPlayer(internalFormat); if(player == null) { player = new Player(internalFormat); } init(); players.add(player); currentPlayer = player; currentPlayer.setLine(line); return player; } /** * Returns the Player that is connected to an internal format. * * @param internalFormat The internal format the Player should have. * @return The Player. */ public Player getPlayer(InternalFormat internalFormat) { for(Player player : players) { if(player.getInternalFormat().equals(internalFormat)) { return player; } } return null; } /** * Remove the Player that has <code>internalFormat</code> as internal format. * * @param internalFormat an <code>InternalFormat</code> value */ public void remove(InternalFormat internalFormat) { Player player = getPlayer(internalFormat); player.stop(); if(player.equals(currentPlayer)) { currentPlayer = null; } players.remove(player); internalFormat.close(); } /** * Returns the current player. * * @return The current player. */ public Player getCurrentPlayer() { return currentPlayer; } public static PlayerHandler getInstance() { if(instance == null) { instance = new PlayerHandler(); } return instance; } }